﻿using System;
using System.Data;
using System.Collections;
using System.Text;
using System.Configuration;

namespace BizElements.Core
{
    #region Documentation.
    /// <summary>
    /// Specifies a subquery to test for the existence of rows.
    /// </summary>
    /// <remarks>ExistsPredicate cannot be created by using its constructor. 
    /// Use <see cref="PredicateFactory"/> to create predicates.
    /// <seealso cref="SearchCondition"/></remarks>
    /// <example>	
    /// The following example deletes all employees that are not referenced in the EmployeeTerritories table.
    /// <code>
    /// public void DeleteNonReferencedEmployees()
    /// {
    /// 	// This is the equivalent SQL code that does the same task.
    /// 
    /// 	//	DELETE FROM Employees
    /// 	//	WHERE NOT EXISTS 
    /// 	//		(SELECT * 
    /// 	//		FROM EmployeeTerritories 
    /// 	//		WHERE (EmployeeTerritories.EmployeeID = Employees.EmployeeID))
    /// 
    /// 	EmployeesMeta employees = new EmployeesMeta();
    /// 	EmployeeTerritoriesMeta empTerritories = new EmployeeTerritoriesMeta();
    /// 
    /// 	// Subquery that returns all referenced employees. The subquery is used by the NOT EXIST predicate.
    /// 	SelectStatement referencedEmployees = new SelectStatement(empTerritories);
    /// 	referencedEmployees.Where.Add(PredicateFactory.Compare(empTerritories.EmployeeID, "=", employees.EmployeeID));
    /// 
    /// 	DeleteStatement delete = new DeleteStatement(employees);
    /// 	// NOT EXISTS predicate is created when the parameter negate of the PredicateFactory.Exists is set to true.
    /// 	delete.Where.Add(PredicateFactory.Exists(referencedEmployees, true));
    /// 	delete.Execute();
    /// }
    /// </code>
    /// </example>
    #endregion
    [Serializable]
    public sealed class ExistsPredicate : PredicateBase
    {
        #region EXISTS.

        /// <summary>
        /// Creates a new EXISTS predicate.
        /// </summary>
        /// <param name="item">Items/expression.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT EXISTS).</param>
        internal ExistsPredicate(PredicateItem item, bool negate)
        {
            if (item.ItemType != SqlItemType.SubQuery)
                throw new ArgumentException(Messages.ExistsPredicate_OnlySubqueriesAreAllowed, "item");

            PredicateItems = new PredicateItem[] { item };
            Negate = negate;
        }

        /// <summary>
        /// Renders predicate as SQL statement.
        /// </summary>
        /// <param name="dbms">Target DBMS.</param>
        /// <param name="output">StringBuilder to which SQL is appended.</param>
        /// <param name="parameters">SQL parameter collection to which the object's and its children's
        /// parameters are added. After the rendering is done the collection contains all parameters with unique names.</param>
        public override void Render(DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
        {
            output.Append("(");
            if (Negate)
                output.Append("NOT EXISTS (");
            else
                output.Append("EXISTS (");

            PredicateItems[0].Render(dbms, output, parameters);
            output.Append("))");
        }

        #endregion
    }
}
